#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>

int main(int argc, char **argv) {
    cv::Mat mat = cv::imread("/home/xlll/Downloads/opencv/samples/data/lena.jpg", cv::IMREAD_GRAYSCALE);
    if (mat.empty()) {
        std::cout << "Failed to read image!" << std::endl;
        return EXIT_FAILURE;
    }
    
    int ksize = 1;
    cv::Mat k1 = (cv::Mat_<float>(3, 3) << 0, 1, 0, 1, -4, 1, 0, 1, 0);
    cv::Mat laplacian1, laplacian2;
    cv::Laplacian(mat, laplacian1, CV_8UC1, ksize, 1.0, 0.0, cv::BORDER_REFLECT101);
    cv::filter2D(mat, laplacian2, CV_8UC1, k1, cv::Point(-1, -1), 0.0, cv::BORDER_REFLECT101);
    cv::Mat diff1 = laplacian1 != laplacian2;
    std::vector<cv::Point> nonzero1;
    cv::findNonZero(diff1, nonzero1);
    std::cout << "k1 = \n" << k1 << std::endl;
    std::cout << "nonzero1 size = " << nonzero1.size() << std::endl << std::endl;
    cv::imshow("ksize = 1", laplacian1);
    
    ksize = 3;
    cv::Mat k3 = (cv::Mat_<float>(3, 3) << 2, 0, 2, 0, -8, 0, 2, 0, 2);
    cv::Laplacian(mat, laplacian1, CV_8UC1, ksize, 1.0, 0.0, cv::BORDER_REFLECT101);
    cv::filter2D(mat, laplacian2, CV_8UC1, k3, cv::Point(-1, -1), 0.0, cv::BORDER_REFLECT101);
    cv::Mat diff2 = laplacian1 != laplacian2;
    std::vector<cv::Point> nonzero2;
    cv::findNonZero(diff2, nonzero2);
    std::cout << "k3 = \n" << k3 << std::endl;
    std::cout << "nonzero2 size = " << nonzero2.size() << std::endl << std::endl;
    cv::imshow("ksize = 3", laplacian1);
    
    ksize = 5;
    cv::Laplacian(mat, laplacian1, CV_8UC1, ksize, 1.0, 0.0, cv::BORDER_REFLECT101);
    cv::Mat x_kx, x_ky, y_kx, y_ky;
    cv::getDerivKernels(x_kx, x_ky, 2, 0, ksize, false, CV_32FC1);
    cv::getDerivKernels(y_kx, y_ky, 0, 2, ksize, false, CV_32FC1);
    cv::Mat x_k = x_ky * x_kx.t();
    cv::Mat y_k = y_ky * y_kx.t();
    std::cout << "x_kx = \n" << x_kx << std::endl;
    std::cout << "x_ky = \n" << x_ky << std::endl;
    std::cout << "x_k = \n" << x_k << std::endl;
    std::cout << "y_kx = \n" << y_kx << std::endl;
    std::cout << "y_ky = \n" << y_ky << std::endl;
    std::cout << "y_k = \n" << y_k << std::endl << std::endl;
    cv::Mat d2x1, d2y1, d2x2, d2y2;
    cv::sepFilter2D(mat, d2x1, CV_32FC1, x_kx, x_ky, cv::Point(-1, -1), 0.0, cv::BORDER_REFLECT101);
    cv::sepFilter2D(mat, d2y1, CV_32FC1, y_kx, y_ky, cv::Point(-1, -1), 0.0, cv::BORDER_REFLECT101);
    cv::filter2D(mat, d2x2, CV_32FC1, x_k, cv::Point(-1, -1), 0.0, cv::BORDER_REFLECT101);
    cv::filter2D(mat, d2y2, CV_32FC1, y_k, cv::Point(-1, -1), 0.0, cv::BORDER_REFLECT101);
    cv::add(d2x1, d2y1, laplacian2, cv::noArray(), CV_8UC1);
    cv::Mat laplacian3;
    cv::add(d2x2, d2y2, laplacian3, cv::noArray(), CV_8UC1);
    cv::Mat diff3 = laplacian1 != laplacian2;
    cv::Mat diff4 = laplacian1 != laplacian3;
    std::vector<cv::Point> nonzero3, nonzero4;
    cv::findNonZero(diff3, nonzero3);
    cv::findNonZero(diff4, nonzero4);
    std::cout << "nonzero3 size = " << nonzero3.size() << std::endl;
    std::cout << "nonzero4 size = " << nonzero4.size() << std::endl;
    cv::imshow("ksize = 5", laplacian1);
    
    cv::waitKey(0);
    
    return EXIT_SUCCESS;
}
